#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
纯requests课程表数据采集器
不依赖Selenium，直接使用requests和cookies
"""

import requests
import json
import time
import logging
from typing import List, Dict, Any, Optional
from datetime import datetime

# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

class PureScheduleScraper:
    """纯requests课程表数据采集器"""
    
    def __init__(self):
        self.session = requests.Session()
        self.base_url = "https://ehall.wynu.edu.cn"
        self.cookies = {}
        self.headers = {
            'Accept': 'application/json, text/javascript, */*; q=0.01',
            'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
            'Connection': 'keep-alive',
            'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
            'Origin': 'https://ehall.wynu.edu.cn',
            'Sec-Fetch-Dest': 'empty',
            'Sec-Fetch-Mode': 'cors',
            'Sec-Fetch-Site': 'same-origin',
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36 Edg/140.0.0.0',
            'X-Requested-With': 'XMLHttpRequest',
            'sec-ch-ua': '"Chromium";v="140", "Not=A?Brand";v="24", "Microsoft Edge";v="140"',
            'sec-ch-ua-mobile': '?0',
            'sec-ch-ua-platform': '"Windows"'
        }
        self.session.headers.update(self.headers)
        
    def set_cookies_from_string(self, cookie_string: str) -> None:
        """从cookie字符串设置cookies"""
        cookies = {}
        for item in cookie_string.split(';'):
            if '=' in item:
                key, value = item.strip().split('=', 1)
                cookies[key] = value
                self.session.cookies.set(key, value, domain='.wynu.edu.cn')
        
        self.cookies.update(cookies)
        logger.info(f"已设置 {len(cookies)} 个cookies")
    
    def get_class_list(self, year: str = "", college_code: str = "", major_code: str = "", semester: str = "2025-2026-1") -> List[Dict[str, Any]]:
        """获取班级列表（自动分页，返回全量）
        :param semester: 学年学期代码，例如 '2024-2025-2'
        """
        url = f"{self.base_url}/jwapp/sys/kcbcx/modules/bjkcb/bjcx.do"
        
        query_setting = []

        # 只在有年级参数时添加年级筛选
        if year:
            query_setting.append({
                "name": "NJ",
                "caption": "年级",
                "linkOpt": "AND",
                "builderList": "cbl_String",
                "builder": "equal",
                "value": year,
                "value_display": f"{year}级"
            })

        # 只在有学院代码时添加学院筛选
        if college_code:
            query_setting.append({
                "name": "YXDM",
                "caption": "院系",
                "linkOpt": "AND",
                "builderList": "cbl_List",
                "builder": "equal",
                "value": college_code,
                "value_display": f"学院代码{college_code}"
            })

        # 始终添加已排课筛选
        query_setting.append({
            "name": "SFYPK",
            "caption": "是否已排课",
            "linkOpt": "AND",
            "builderList": "cbl_String",
            "builder": "equal",
            "value": "1",
            "value_display": "是"
        })
        
        # 如果指定了专业代码，添加专业筛选条件
        if major_code:
            query_setting.append({
                "name": "ZYDM",
                "caption": "专业",
                "linkOpt": "AND",
                "builderList": "cbl_String",
                "builder": "equal",
                "value": major_code,
                "value_display": f"专业代码{major_code}"
            })
        
        page_size = 100
        page_number = 1
        all_rows: List[Dict[str, Any]] = []

        try:
            while True:
                data = {
                    'XNXQDM': semester,
                    'SFSY': '1',
                    '*order': '-NJ,+YXPX,+ZYPX,+PX',
                    'SFYPK': '1',
                    'querySetting': json.dumps(query_setting, ensure_ascii=False),
                    'pageSize': str(page_size),
                    'pageNumber': str(page_number)
                }

                response = self.session.post(url, data=data)
                response.raise_for_status()
                result = response.json()

                if result.get('code') != '0' or 'datas' not in result:
                    logger.error(f"获取班级列表失败: {result}")
                    break

                class_data = result['datas'].get('bjcx', {})
                rows = class_data.get('rows', [])
                total_size = class_data.get('totalSize', 0)

                if page_number == 1:
                    logger.info(f"成功获取 {len(rows)} 个班级，总计 {total_size} 个班级")

                all_rows.extend(rows)

                # 判断是否还有下一页
                if len(all_rows) >= total_size or len(rows) < page_size:
                    break

                page_number += 1
                time.sleep(0.2)

            logger.info(f"已累计获取 {len(all_rows)}/{total_size if 'total_size' in locals() else len(all_rows)} 个班级（学期: {semester}）")
            return all_rows

        except Exception as e:
            logger.error(f"获取班级列表时出错: {str(e)}")
            return []
    
    def get_class_schedule(self, class_id: str, class_name: str, semester: str = "2025-2026-1") -> Optional[Dict[str, Any]]:
        """获取指定班级的课程表"""
        url = f"{self.base_url}/jwapp/sys/kcbcx/modules/bjkcb/bjkcb.do"
        
        data = {
            'XNXQDM': semester,
            'BJDM': class_id,
            'pageSize': '1000',
            'pageNumber': '1'
        }
        
        try:
            response = self.session.post(url, data=data)
            response.raise_for_status()
            
            result = response.json()
            
            if result.get('code') == '0' and 'datas' in result:
                schedule_data = result['datas'].get('bjkcb', {})
                schedules = schedule_data.get('rows', [])
                
                logger.info(f"成功获取班级 {class_name} 的课程表，共 {len(schedules)} 条记录")
                return {
                    'class_id': class_id,
                    'class_name': class_name,
                    'semester': semester,
                    'schedules': schedules,
                    'total_count': len(schedules)
                }
            else:
                logger.error(f"获取班级 {class_name} 课程表失败: {result}")
                return None
                
        except Exception as e:
            logger.error(f"获取班级 {class_name} 课程表时出错: {str(e)}")
            return None
    
    def get_teacher_list(self, semester: str = "2025-2026-1", teacher_name: str = "", teacher_id: str = "") -> List[Dict[str, Any]]:
        """获取教师列表"""
        url = f"{self.base_url}/jwapp/sys/kcbcx/modules/jshkcb/cxypkjs.do"
        
        query_setting = [
            {
                "name": "SFYPK",
                "caption": "是否已排课",
                "linkOpt": "AND",
                "builderList": "cbl_String",
                "builder": "equal",
                "value": "1",
                "value_display": "是"
            }
        ]
        
        # 如果指定了教师姓名，添加姓名筛选条件
        if teacher_name:
            query_setting.append({
                "name": "XM",
                "caption": "姓名",
                "linkOpt": "AND",
                "builderList": "cbl_String",
                "builder": "like",
                "value": teacher_name,
                "value_display": teacher_name
            })
        
        # 如果指定了教师工号，添加工号筛选条件
        if teacher_id:
            query_setting.append({
                "name": "ZGH",
                "caption": "教师号",
                "linkOpt": "AND",
                "builderList": "cbl_String",
                "builder": "equal",
                "value": teacher_id,
                "value_display": teacher_id
            })
        
        data = {
            'XNXQDM': semester,
            'querySetting': json.dumps(query_setting, ensure_ascii=False),
            '*order': '+PX,+SZDWDM',
            'pageSize': '1000',
            'pageNumber': '1'
        }
        
        try:
            response = self.session.post(url, data=data)
            response.raise_for_status()
            
            result = response.json()
            
            if result.get('code') == '0' and 'datas' in result:
                teacher_data = result['datas'].get('cxypkjs', {})
                teachers = teacher_data.get('rows', [])
                total_size = teacher_data.get('totalSize', 0)
                
                logger.info(f"成功获取 {len(teachers)} 个教师，总计 {total_size} 个教师")
                return teachers
            else:
                logger.error(f"获取教师列表失败: {result}")
                return []
                
        except Exception as e:
            logger.error(f"获取教师列表时出错: {str(e)}")
            return []
    
    def get_teacher_schedule(self, teacher_id: str, teacher_name: str, semester: str = "2025-2026-1") -> Optional[Dict[str, Any]]:
        """获取指定教师的课程表"""
        url = f"{self.base_url}/jwapp/sys/kcbcx/modules/jshkcb/jskcb.do"
        
        data = {
            'XNXQDM': semester,
            'JSH': teacher_id,
            'pageSize': '1000',
            'pageNumber': '1'
        }
        
        try:
            response = self.session.post(url, data=data)
            response.raise_for_status()
            
            result = response.json()
            
            if result.get('code') == '0' and 'datas' in result:
                schedule_data = result['datas'].get('jskcb', {})
                schedules = schedule_data.get('rows', [])
                
                logger.info(f"成功获取教师 {teacher_name} 的课程表，共 {len(schedules)} 条记录")
                return {
                    'teacher_id': teacher_id,
                    'teacher_name': teacher_name,
                    'semester': semester,
                    'schedules': schedules,
                    'total_count': len(schedules)
                }
            else:
                logger.error(f"获取教师 {teacher_name} 课程表失败: {result}")
                return None
                
        except Exception as e:
            logger.error(f"获取教师 {teacher_name} 课程表时出错: {str(e)}")
            return None
    
    def scrape_all_class_schedules(self, year: str = "", college_code: str = "",
                                 major_code: str = "", semester: str = "2025-2026-1") -> List[Dict[str, Any]]:
        """采集所有班级的课程表数据"""
        # 构建采集范围描述
        scope_parts = []
        if year:
            scope_parts.append(f'{year}级')
        if college_code:
            scope_parts.append('指定学院')
        else:
            scope_parts.append('全校')
        if major_code:
            scope_parts.append('指定专业')

        scope_desc = ' '.join(scope_parts) + '所有班级'
        logger.info(f"开始采集 {scope_desc} 的课程表数据...")
        
        # 1. 获取班级列表
        classes = self.get_class_list(year, college_code, major_code, semester)
        if not classes:
            logger.error("无法获取班级列表，终止采集")
            return []
        
        # 2. 采集每个班级的课程表
        all_schedules = []
        total_classes = len(classes)
        
        for i, class_info in enumerate(classes, 1):
            class_id = class_info.get('BJDM', '')
            class_name = class_info.get('BJMC', '')
            
            if not class_id:
                logger.warning(f"班级信息缺少班级代码，跳过: {class_info}")
                continue
            
            logger.info(f"正在采集第 {i}/{total_classes} 个班级: {class_name}")
            
            schedule_data = self.get_class_schedule(class_id, class_name, semester)
            if schedule_data:
                all_schedules.append(schedule_data)
            
            # 添加延迟避免请求过于频繁
            time.sleep(0.5)
        
        logger.info(f"班级课程表采集完成，共采集 {len(all_schedules)} 个班级的课程表数据")
        return all_schedules
    
    def scrape_all_teacher_schedules(self, semester: str = "2025-2026-1", teacher_name: str = "", teacher_id: str = "") -> List[Dict[str, Any]]:
        """采集所有教师的课程表数据"""
        logger.info(f"开始采集所有教师的课程表数据...")
        
        # 1. 获取教师列表
        teachers = self.get_teacher_list(semester, teacher_name, teacher_id)
        if not teachers:
            logger.error("无法获取教师列表，终止采集")
            return []
        
        # 2. 采集每个教师的课程表
        all_schedules = []
        total_teachers = len(teachers)
        
        for i, teacher_info in enumerate(teachers, 1):
            teacher_id = teacher_info.get('ZGH', '')
            teacher_name = teacher_info.get('XM', '')
            department = teacher_info.get('SZDWDM_DISPLAY', '')
            
            if not teacher_id or not teacher_name:
                logger.warning(f"教师信息缺少必要字段，跳过: {teacher_info}")
                continue
            
            logger.info(f"正在采集第 {i}/{total_teachers} 个教师: {teacher_name} ({department})")
            
            schedule_data = self.get_teacher_schedule(teacher_id, teacher_name, semester)
            if schedule_data:
                # 添加教师部门信息
                schedule_data['department'] = department
                schedule_data['teacher_title'] = teacher_info.get('ZYJSZWDM_DISPLAY', '')
                all_schedules.append(schedule_data)
            
            # 添加延迟避免请求过于频繁
            time.sleep(0.5)
        
        logger.info(f"教师课程表采集完成，共采集 {len(all_schedules)} 个教师的课程表数据")
        return all_schedules
    
    def print_summary(self, data: List[Dict[str, Any]], data_type: str = "课程表") -> None:
        """打印数据摘要"""
        if not data:
            logger.info("没有数据可显示")
            return
        
        total_items = len(data)
        total_courses = sum(item.get('total_count', 0) for item in data)
        
        print("\n" + "="*50)
        print(f"{data_type}数据采集摘要")
        print("="*50)
        print(f"总数量: {total_items}")
        print(f"总课程记录数: {total_courses}")
        print("\n详情:")
        
        for item in data[:10]:  # 只显示前10个
            if 'class_name' in item:
                name = item.get('class_name', '未知班级')
                count = item.get('total_count', 0)
                print(f"  - {name}: {count} 条课程记录")
            elif 'teacher_name' in item:
                name = item.get('teacher_name', '未知教师')
                count = item.get('total_count', 0)
                dept = item.get('department', '未知部门')
                print(f"  - {name} ({dept}): {count} 条课程记录")
        
        if total_items > 10:
            print(f"  ... 还有 {total_items - 10} 个")
        
        print("="*50)


# 全局采集器实例
scraper = PureScheduleScraper()

def set_cookies(cookie_string: str):
    """设置cookies"""
    scraper.set_cookies_from_string(cookie_string)

def scrape_classes(year: str = "", college_code: str = "", major_code: str = "", semester: str = "2025-2026-1", app_context=None) -> bool:
    """采集班级课程表并保存到数据库"""
    try:
        schedules = scraper.scrape_all_class_schedules(year, college_code, major_code, semester)
        if schedules:
            # 保存到数据库
            if app_context:
                with app_context:
                    from models import ScheduleManager
                    count = ScheduleManager.save_class_data(schedules)
                    logger.info(f"成功保存 {count} 条班级课程记录到数据库")
            else:
                from models import ScheduleManager
                count = ScheduleManager.save_class_data(schedules)
                logger.info(f"成功保存 {count} 条班级课程记录到数据库")

            scraper.print_summary(schedules, "班级课程表")
            return True
        else:
            return False
    except Exception as e:
        logger.error(f"采集班级课程表失败: {str(e)}")
        return False

def scrape_teachers(semester: str = "2025-2026-1", teacher_name: str = "", teacher_id: str = "", app_context=None) -> bool:
    """采集教师课程表并保存到数据库"""
    try:
        schedules = scraper.scrape_all_teacher_schedules(semester, teacher_name, teacher_id)
        if schedules:
            # 保存到数据库
            if app_context:
                with app_context:
                    from models import ScheduleManager
                    count = ScheduleManager.save_teacher_data(schedules)
                    logger.info(f"成功保存 {count} 条教师课程记录到数据库")
            else:
                from models import ScheduleManager
                count = ScheduleManager.save_teacher_data(schedules)
                logger.info(f"成功保存 {count} 条教师课程记录到数据库")

            scraper.print_summary(schedules, "教师课程表")
            return True
        else:
            return False
    except Exception as e:
        logger.error(f"采集教师课程表失败: {str(e)}")
        return False
